package com.wolfeyes.common.utils.reflect.proxy.cglibproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Date;

//import org.apache.log4j.Logger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.wolfeyes.common.core.enums.common.DateFormatTypeEnum;
import com.wolfeyes.common.utils.datetime.DateUtilss;
import com.wolfeyes.common.utils.redis.RedisUtils;
import com.wolfeyes.common.utils.reflect.proxy.jdkproxy.simple.Person;

//import groovy.transform.stc.FirstParam.ThirdGenericType;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
 * 
 * @title 代理模式的英文叫做Proxy或Surrogate，中文都可译为”代理“，场景如下：
 * 1.代理类对象实例内部含有对被代理真实类对象实例的引用，从而可以在任何时候操作被代理真实类对象实例；
 * 2.代理类对象实例提供一个与被代理真实类对象实例相同的接口，以便可以在任何时候都可以替代被代理真实类对象实例
 *   控制对被代理真实类对象实例的引用，负责在需要的时候创建被代理真实类对象实例（和删除被代理真实类对象实例）；
 * 3.代理类对象实例通常在将客户端调用传递给被代理真实类对象实例之前或之后，都要执行某个操作，
 *   而不是单纯简单地将调用传递给被代理真实类对象实例。 <br/>
 *   
 * cglib动态代理
 * 1.CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
 * 2.用CGlib生成代理类是目标类的子类。
 * 3.用CGlib生成 代理类不需要接口。
 * 4.用CGLib生成的代理类重写了父类的各个方法。
 * 5.拦截器中的intercept方法内容正好就是代理类中的方法体。
 *  
 *  请注意：
 *  目标类是代理类的父类。
 *  拦截器必须实现MethodInterceptor接口。
 *  而且MethodInterceptor接口中的intercept方法就是代理类的方法体的父级多态引用。
 *  在本例中使用字节码增强机制创建代理对象实例。
 * 处理者(Handler)
 * @description 指派动态代理中方法所要调用的调用处理程序invocationHandler
 * @author yanyl
 * @projectName bootdo
 * @className DynamicProxyHandler.java
 * @author yanyl
 * @email 1991yanyl@163.com
 * @date 2018-07-04 15:53:58
 * @since 2017年02月02日下午5:37:39
 * @createTime 2018年06月09日 下午17:04:13
 * @version 1.0.0
 *
 */
public class CGlibDynamicProxyHandler implements MethodInterceptor {

	/**
	 * slf4j日志对象
	 */
	private static final Logger logger = LoggerFactory.getLogger(CGlibDynamicProxyHandler.class);
	// private Logger logger = Logger.getLogger(DynamicProxyHandler.class);

	/**
	 * 被代理的目标对象实例targetObject
	 */
	private Object targetObject = null;

	/**
	 * @title 快速获取被代理的目标对象实例targetObject的引用
	 * @see {@link Person #Person()}
	 * @description TODO(随机生成编号)
	 * @param arg 参数说明
	 * @param targetObject 被代理的目标对象实例
	 * @author yanyl
	 * @since 2018年07月12日上午11:37:39
	 * @return 返回类型 void
	 * @throws Exception
	 */
	public void setTarget(Object targetObject) {

		this.targetObject = targetObject;

	}

    /**
     * 默认构造方法
     */
	public CGlibDynamicProxyHandler() {
    	
		// 先构造父类对象BasicDataSource
    	super();
    	
    }
	
    /**
     * 有参构造方法
     */
	public CGlibDynamicProxyHandler(Object targetObject) {
    	
		// 先构造父类对象BasicDataSource
    	super();
    	
		// 设置被代理的目标对象实例targetObject的引用
		this.setTarget(targetObject);
    	
    }
	
	/**
	 * @title 创建被代理的目标对象实例targetObject的代理类对象实例
	 * @see {@link Person #Person()}
	 * @description TODO(随机生成编号)
	 * @param arg 参数说明
	 * @param targetObject 被代理的目标对象实例
	 * @author yanyl
	 * @since 2018年07月12日上午11:37:39
	 * @return 返回类型 void
	 * @throws Exception
	 */
	public Object createProxyInstance() {
		
		// 该类用于生成代理对象实例
		Enhancer enhancer = new Enhancer();
		
		// 设置父类
		enhancer.setSuperclass(this.targetObject.getClass());
		
		// 设置回调对象为本身
		enhancer.setCallback(this);
		
		// 创建代理类对象实例
		Object proxyObject = enhancer.create();
		
		// 返回代理类对象实例
		return proxyObject;
	}
	
	/**
	 * @title 创建被代理的目标对象实例targetObject的代理类对象实例
	 * @see {@link Person #Person()}
	 * @description TODO(随机生成编号)
	 * @param arg 参数说明
	 * @param targetObject 被代理的目标对象实例
	 * @author yanyl
	 * @since 2018年07月12日上午11:37:39
	 * @return 返回类型 void
	 * @throws Exception
	 */
	public Object createProxyInstance(Object targetObject) {
		
		// 设置被代理的目标对象实例targetObject的引用
		this.setTarget(targetObject);
		
		// 该类用于生成代理对象实例
		Enhancer enhancer = new Enhancer();
		
		// 设置父类
		enhancer.setSuperclass(this.targetObject.getClass());
		
		// 设置回调对象为本身
		enhancer.setCallback(this);
		
		// 创建代理类对象实例
		Object proxyObject = enhancer.create();
		
		// 返回代理类对象实例
		return proxyObject;
	}
	
	/**
	 * @title 快速封装处理被代理的目标对象实例targetObject的方法调用并返回结果
	 * @see {@link Method}
	 * @description TODO(随机生成编号)
	 * @param arg 参数说明
	 * @param proxy 被代理的目标对象的代理类实例
	 * @param method (父类)被代理的目标对象实例targetObject的方法实例
	 * @param args 传入被代理的目标对象实例targetObject上方法调用的参数值的对象数组
	 * @param methodProxy 代理实例上调用(父类)被代理的目标对象实例targetObject的方法实例
	 * @author yanyl
	 * @since 2018年07月12日上午11:37:39
	 * @return 返回类型 void
	 * @throws Exception
	 */
	@Override
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) 
			throws Throwable {
		
		// 快速获取长格式的时间字符串；
		String startTime = DateUtilss.getTimeNow();
		
		// 打印控制台
		logger.info("执行开始时间:" + startTime);
		
		System.out.println("代理类：" + proxy.getClass());
		
		System.out.println("方法名称:" + method.getName());
		
		// 方法的参数值
		if(args != null && args.length > 0) {
			
			// 遍历(参数列表)参数值的对象数组
			for(int index = 0; index < args.length; index ++) {
				
				System.out.println("方法参数值：" + args[index]);
			}
		}
		
		// 在代理实例上处理方法调用并返回结果。
		// 即：方法的返回值，无返回类型(void)时，为null。
		//Object result = method.invoke(this.targetObject, args);
		Object result = methodProxy.invoke(this.targetObject, args);
		
		// 快速获取长格式的时间字符串；
		String endTime = DateUtilss.getTimeNow();
		
		// 打印到控制台
		logger.info("执行结束时间:" + endTime);
		
		// 返回method执行结果
		return result;
		
	}
	
	/**
	 * 测试方法main()
	 * @param args 参数数组
	 */
	public static void main(String[] args) {

		// 开始测试时间：startTime
		long startTime = System.currentTimeMillis();
		
		/*开始测试：
		 * */
		
		
		// 测试场景一：
		
		// 被代理类PersonCase对象实例
		PersonCase personCase = new PersonCase();
		
		// 调用默认构造方法，创建代理类对象实例生成器
		CGlibDynamicProxyHandler cGlibDynamicProxyHandler = new CGlibDynamicProxyHandler();
		
		// 创建代理类对象实例
		Object proxyObject = cGlibDynamicProxyHandler.createProxyInstance(personCase);
		
		// 强转为(父类)被代理的目标对象实例
		PersonCase proxyPersonCase = (PersonCase)proxyObject;
		
		proxyPersonCase.eating();
		
		proxyPersonCase.sleep();
		
		
		// 测试场景二：
		
		// 调用有参构造方法，创建代理类对象实例生成器
		CGlibDynamicProxyHandler cGlibDynamicProxyHandler2 = new CGlibDynamicProxyHandler(personCase);
		
		// 创建代理类对象实例
		Object proxyObject2 = cGlibDynamicProxyHandler2.createProxyInstance();
		
		// 强转为(父类)被代理的目标对象实例
		PersonCase proxyPersonCase2 = (PersonCase)proxyObject2;
		
		proxyPersonCase2.eating();
		
		proxyPersonCase2.sleep();
		
		// 结束测试时间：startTime
		long endTime = System.currentTimeMillis();
		
		// 耗时：单位（秒）
		System.out.println("用时:" + (endTime - startTime) / 1000d + "秒");
		
	}

}

